#include "ftpclient.h"
#include "protocol.h"

//发送一个bye的请求给服务器
void cmd_bye(int sockfd)
{
    //客户端向服务器发送bye请求
    //包头(1)	包长度(4)	 命令号(4)  包尾(1)
    char cmd[1024] = {0};
    int pkg_len = 8;
    enum CMD_NO cmd_no = FTP_CMD_BYE;
    int i = 0;
    cmd[i++] = 0xc0;//包头
         
     cmd[i++] = 0xff & pkg_len; // 存放pkg_len的第一个字节
    cmd[i++] = 0xff & (pkg_len >> 8);
    cmd[i++] = 0xff & (pkg_len >> 16);
    cmd[i++] = 0xff & (pkg_len >> 24);

    cmd[i++] = 0xff & cmd_no; // 存放cmd_no的第一个字节
    cmd[i++] = 0xff & (cmd_no >> 8);
    cmd[i++] = 0xff & (cmd_no >> 16);
    cmd[i++] = 0xff & (cmd_no >> 24);

    cmd[i++] = 0xc0; // 包尾

    int r = write(sockfd,cmd,i);
    if(r != i)
    {
        printf("write bye cmd failed\n");
        return;
    }
}

//发送一个ls请求给服务器
void cmd_ls(int sockfd)
{
    //向服务器发送ls请求
    //包头(1) 包长度(4) 命令号(4) 包尾(1) char cmd[1024] = {0}; // 表示我要发送的具体的数据序列
    unsigned char cmd[1024] = {0};
    int pkg_len = 8;
    enum CMD_NO cmd_no = FTP_CMD_LS;
    int i = 0;
    cmd[i++] = 0xc0; // 包头

     cmd[i++] = 0xff & pkg_len; // 存放pkg_len的第一个字节
    cmd[i++] = 0xff & (pkg_len >> 8);
    cmd[i++] = 0xff & (pkg_len >> 16);
    cmd[i++] = 0xff & (pkg_len >> 24);

    cmd[i++] = 0xff & cmd_no; // 存放cmd_no的第一个字节
    cmd[i++] = 0xff & (cmd_no >> 8);
    cmd[i++] = 0xff & (cmd_no >> 16);
    cmd[i++] = 0xff & (cmd_no >> 24);

    cmd[i++] = 0xc0; // 包尾

    int r = write(sockfd,cmd,i);
    if(r != i)
    {
        printf("write ls cmd failed\n");
        return;
    }
    //printf("send ls cmd success\n");

}
// 处理服务器的ls 回复数据
void recv_ls_resp(int sockfd)
{
    //ls请求的回复
    //包头(1) 包长度(4) 命令号(4) 回复长度(4) 结果(1) 回复内容(文件名列表) 包尾(1)
    unsigned char cmd[8192] = {0};
    int i = 0;
    int r;
    unsigned char ch;
    
    do
    {
        r = read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    }while(ch != 0xc0);
    
    //防止读取到了上一次残余数据包的包尾
    while(ch == 0xc0)
    {
        r = read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    }// 循环结束了,ch肯定不是0xc0,而是数据包中的第一个有效字节

    // 一次性的把整个数据包读取到cmd中
    i = 0;
    while(ch != 0xc0)
    {
        cmd[i++] = ch;
        r = read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    }
    
    //做一个简单的校验(数据包长度校验)
    int pkg_len = cmd[0] | cmd[1] << 8 | cmd[2] << 16 | cmd[3] << 24;
    if(i != pkg_len)// 实际读取到的字节数量i 和理论上数量pkg_len 不一致,直接丢弃
    {
        printf("pkg_len = %d,read len = %d\n",pkg_len,i);
        return;
    }
    
    //解析当前命令号
    int cmd_no = cmd[4] | cmd[5] << 8 | cmd[6] << 16 | cmd[7] << 24;
    if(cmd_no != FTP_CMD_LS)
    {
        printf("recv_ls_resp : cmd_no = %d\n",cmd_no);
        return ;
    } 
    
    if(cmd[12] == 1)  //获取失败,解析服务器的错误码
    {
        printf("FTP_CMD_LS failed\n");
        int errnum = cmd[13] | cmd[14] << 8 | cmd[15] << 16 | cmd[16] << 24;
        printf("%s\n",strerror(errnum));
    }else 
    {
        printf("%s\n",cmd+13);
    }
}

void cmd_get(int sockfd,const char *filename)
{
    //cmd:get filename
    //包头(1) 包长度(4) 命令号(4) 参数长度(4) 参数内容(文件名长度) 包尾(1) 
    char cmd[1024] = {0};
    int arglen = strlen(filename);
    int pkg_len = 4 + 4 + 4 + arglen;
    enum CMD_NO cmd_no = FTP_CMD_GET;
    int i = 0;
    cmd[i++] = 0xc0;//包头

    cmd[i++] = 0xff & pkg_len; // 存放pkg_len的第一个字节
    cmd[i++] = 0xff & (pkg_len >> 8);
    cmd[i++] = 0xff & (pkg_len >> 16);
    cmd[i++] = 0xff & (pkg_len >> 24);

    cmd[i++] = 0xff & cmd_no; // 存放cmd_no的第一个字节
    cmd[i++] = 0xff & (cmd_no >> 8);
    cmd[i++] = 0xff & (cmd_no >> 16);
    cmd[i++] = 0xff & (cmd_no >> 24);

    cmd[i++] = 0xff & arglen; // 存放arglen的第一个字节
    cmd[i++] = 0xff & (arglen >> 8);
    cmd[i++] = 0xff & (arglen >> 16);
    cmd[i++] = 0xff & (arglen >> 24);
    //具体的参数，就是要获得文件的文件名
    int j = 0;
    for(j = 0;j<arglen;j++)
    {
        cmd[i++] = filename[j];
    }
    
    cmd[i++] = 0xc0;//包尾

    int r = write(sockfd,cmd,i);
    if(r != i)
    {
        printf("send ls cmd failed\n");
        return;
    }
}

void recv_get_resp(int sockfd,const char *filename)
{
    // get请求的回复
    // 包头(1)	包长度(4)	命令号(4)	回复长度(4)   结果(1)	 回复内容(文件的长度) 包尾(1)  [ 文件内容]
    unsigned char cmd[20] = {0};
    int i = 0;
    //读取服务器的回复
    //找到第一个0xc0
    int r;
    unsigned char ch;
    do
    {
        r = read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    } while (ch != 0xc0);

    //防止读取到上一个残余的数据包中的残余
    while(ch == 0xc0)
    {
        r = read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    }//循环结束，ch肯定不是0xc0，而是2数据包中的第一个有效字节
    //一次性把整个数据包读取到cmd中
    i = 0;
    while(ch != 0xc0)
    {
        cmd[i++] = ch;
        r =read(sockfd,&ch,1);
        if(r <= 0)
        {
            perror("server dead\n");
            exit(0);
        }
    }

    //做一个简单的校验（数据包长度校验）
    int pkg_len = cmd[0] | cmd[1] << 8 | cmd[2] << 16 | cmd[3] << 24;
    if(i != pkg_len)//实际读取到的字节数量i 和理论上数量pkg_len 不一致,直接丢弃
    {
          printf("pkg_len = %d,read len = %d\n", pkg_len, i);
        return;
    }

    // 解析出当前的命令号,根据命令号去完成剩余的操作
    int cmd_no = cmd[4] | cmd[5] << 8 | cmd[6] << 16 | cmd[7] << 24;
    if (cmd_no != FTP_CMD_GET)
    {
        printf("recv_get_resp : cmd_no = %d\n", cmd_no);
        return;
    }

    if (cmd[12] == 1) // 获取失败,解析服务器的错误码
    {
        printf("FTP_CMD_GET failed\n");
        int errnum = cmd[13] | cmd[14] << 8 | cmd[15] << 16 | cmd[16] << 24;
        printf("%s\n", strerror(errnum));
    }
    else //获取成功,准备接收文件的大小和文件的所有内容
    {
        int filesize = cmd[13] | cmd[14] << 8 | cmd[15] << 16 | cmd[16] << 24;
        printf("filesize = %d\n",filesize);
        int fd = open(filename,O_RDWR | O_CREAT | O_TRUNC,0664);
        if(fd == -1)
        {
            perror("in recv_get_resp open failed");
            return;
        }

        int cur_read = 0;
        while(cur_read < filesize)
        {
            unsigned char buf[1024] = {0};
            int r = read(sockfd,buf,1024);
            if(r < 0)
            {
                printf("in recv_get_resp read file failed\n");
                return;
            }else 
            {
                cur_read += r;
                write(fd,buf,r);
            }
        }
        printf("get %s success!\n",filename);
        close(fd);
    }
}

//  向服务器发出推送文件的请求
void cmd_put(int sockfd, const char *filename)
{
    // 验证文件在本地是否存在,并且是一个普通文件,同时读取文件的大小
    //  在发出put数据前,确认文件存在,并且需要获取文件大小
    int filesize = 0;
    struct stat st;
    int r = lstat(filename, &st);
    if (r == -1)
    {
        perror("put filename failed");
        return;
    }
    else if (S_ISREG(st.st_mode))
    {
        filesize = st.st_size;
    }else
    {
        printf("%s not a regular file!\n",filename);
        return;
    }

    //cmd:put filename
    // 包头(1) 包长度(4) 命令号(4) 参数长度(4) 参数1内容(文件名)  参数2内容(文件的大小) 包尾(1)
    char cmd[1024] = {0};
    int arglen = strlen(filename);  //文件名的长度,让服务器可以准确的获取文件名
    int pkg_len = 4 + 4 + 4 + arglen + 4;
    enum CMD_NO cmd_no = FTP_CMD_PUT;
    int i = 0;
    cmd[i++] = 0xc0; // 包头

    cmd[i++] = 0xff & pkg_len; // 存放pkg_len的第一个字节
    cmd[i++] = 0xff & (pkg_len >> 8);
    cmd[i++] = 0xff & (pkg_len >> 16);
    cmd[i++] = 0xff & (pkg_len >> 24);

    cmd[i++] = 0xff & cmd_no; // 存放cmd_no的第一个字节
    cmd[i++] = 0xff & (cmd_no >> 8);
    cmd[i++] = 0xff & (cmd_no >> 16);
    cmd[i++] = 0xff & (cmd_no >> 24);

    cmd[i++] = 0xff & arglen; // 存放arglen的第一个字节
    cmd[i++] = 0xff & (arglen >> 8);
    cmd[i++] = 0xff & (arglen >> 16);
    cmd[i++] = 0xff & (arglen >> 24);
    // 具体的参数,就是要获取的文件的文件名
    int j = 0;
    for (j = 0; j < arglen; j++)
    {
        cmd[i++] = filename[j];
    }

    //文件的大小
    cmd[i++] = 0xff & filesize; // 存放filesize的第一个字节
    cmd[i++] = 0xff & (filesize >> 8);
    cmd[i++] = 0xff & (filesize >> 16);
    cmd[i++] = 0xff & (filesize >> 24);

    cmd[i++] = 0xc0; // 包尾

    r = write(sockfd, cmd, i);
    if (r != i)
    {
        printf("sned put cmd failed\n");
        return;
    }
    //发送文件的实际数据内容

    int fd = open(filename, O_RDONLY);
    if (-1 == fd)
    {
        perror("in cmd_put,open file error");
        return;
    }
    unsigned char buf[1024] = {0};
    while (1)
    {
        memset(buf, 0, 1024);
        size_t r = read(fd, buf, 1024);
        if (r <= 0)
        {
            break;
        }
        write(sockfd, buf, r);
    }
    close(fd);

}